#ifndef STX_THREAD_SAFE_HPP
#define STX_THREAD_SAFE_HPP

#include <stx/thread.hpp>

namespace stx {

template<class T, class lockable_type = recursive_mutex>
class thread_safe;

template<class T, class thread_safe_type>
class thread_safe_proxy;

template<class T>
struct assign_if_pointer_t
{
    static void assign(T& t1, T& t2)
    {
    }
};

template<class T>
struct assign_if_pointer_t<T*>
{
    static void assign(T*& t1, T*& t2)
    {
        t1 = t2;
    }
};

template<class T, class lockable_type>
class thread_safe
{
public:
    
    typedef thread_safe<T, lockable_type> this_type;
    
    typedef thread_safe_proxy<T, this_type> proxy_type;
    
    typedef typename get_pointer_t<T>::type value_ptr_type;
    
    typedef typename get_pointer_t<lockable_type>::type mutex_ptr_type;
    
    thread_safe()
    {
    }
    
    thread_safe(lockable_type& mtx)
    {
        assign_if_pointer_t<lockable_type>::assign(mtx_, mtx);
    }
    
    thread_safe(const T& t): t_(t)
    {
    }
    
    thread_safe(const T& t, mutex_ptr_type mtx): t_(t)
    {
        assign_if_pointer_t<lockable_type>::assign(mtx_, mtx);
    }
    
    proxy_type operator->()
    {
        return proxy_type(*this);
    }
    
    const proxy_type operator->() const
    {
        return proxy_type(*this);
    }
    
    void lock() const
    {
        get_pointer(mtx_)->lock();
    }
    
    void unlock() const
    {
        get_pointer(mtx_)->unlock();
    }
    
    value_ptr_type nolock()
    {
        return get_pointer(t_);
    }
    
    const value_ptr_type nolock() const
    {
        return get_pointer(t_);
    }
    
    value_ptr_type get()
    {
        return get_pointer(t_);
    }
    
    const value_ptr_type get() const
    {
        return get_pointer(t_);
    }
    
private:
    
    T t_;
    
    mutable lockable_type mtx_;
};

template<class T, class thread_safe_type>
class thread_safe_proxy
{
public:
    
    typedef typename thread_safe_type::value_ptr_type value_ptr_type;
    
    thread_safe_proxy(const thread_safe_type& a): thread_safe_(const_cast<thread_safe_type&>(a))
    {
        thread_safe_.lock();
    }
    
    ~thread_safe_proxy()
    {
        thread_safe_.unlock();
    }
    
    value_ptr_type operator->()
    {
        return thread_safe_.get();
    }
    
    const value_ptr_type operator->() const
    {
        return thread_safe_.get();
    }
    
private:
    
    thread_safe_type& thread_safe_;
};

} // namespace stx

#endif // STX_THREAD_SAFE_HPP
